package com.demo2do.fms.admin.service.business.impl;

import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;

import com.demo2do.core.entity.exception.BusinessException;
import com.demo2do.core.entity.page.Page;
import com.demo2do.core.persistence.GenericDaoSupport;
import com.demo2do.core.persistence.annotation.Dao;
import com.demo2do.fms.admin.dao.business.DispatchOrderDao;
import com.demo2do.fms.admin.dao.business.DispatchOrderDaoSupport;
import com.demo2do.fms.admin.dao.business.OtherFeeDaoSupport;
import com.demo2do.fms.admin.dao.business.TransportCostDao;
import com.demo2do.fms.admin.dto.OtherFeeInfo;
import com.demo2do.fms.admin.entity.report.business.DispatchOrderReportSearch;
import com.demo2do.fms.admin.entity.report.business.TransOrderReportSearch;
import com.demo2do.fms.admin.entity.search.business.DispatchOrderSearch;
import com.demo2do.fms.admin.entity.search.business.TransOrderSearch;
import com.demo2do.fms.admin.entity.security.Login;
import com.demo2do.fms.admin.handler.support.FmsJdbcSupport;
import com.demo2do.fms.admin.service.business.DispatchOrderService;
import com.demo2do.fms.entity.business.balance.OtherFee;
import com.demo2do.fms.entity.business.shipment.DispatchOrder;
import com.demo2do.fms.entity.business.shipment.DispatchOrderRecord;
import com.demo2do.fms.entity.business.shipment.TransportCost;
import com.demo2do.fms.entity.data.business.OtherFeeType;
import com.demo2do.fms.entity.driver.Employee;

/**
 * 
 * @author Ysy
 *
 */
@Service("dispatchOrderService")
public class DispatchOrderServiceImpl implements DispatchOrderService {
    
    private static final Logger logger = LoggerFactory.getLogger(DispatchOrderServiceImpl.class);
    
    @Autowired
    private GenericDaoSupport genericDaoSupport;
    
    @Autowired
    private FmsJdbcSupport fmsJdbcSupport;
    
    @Autowired
    private DispatchOrderDaoSupport dispatchOrderDaoSupport;
    
    @Dao
    private DispatchOrderDao dispatchOrderDao;
    
    @Dao
    private TransportCostDao transportCostDao;
    
    @Autowired
    private OtherFeeDaoSupport otherFeeDaoSupport;
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.DispatchOrderService#countDispatch(com.demo2do.fms.admin.entity.search.business.DispatchOrderSearch)
     */
    public int countDispatch(DispatchOrderSearch dispatchOrderSearch) {
        String sentence = dispatchOrderSearch.buildSentenceTemplate();
        Map<String, Object> parameters = dispatchOrderSearch.buildParameters();
        return genericDaoSupport.count(sentence, parameters);
        
    }

    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.DispatchOrderService#listDispatch(com.demo2do.fms.admin.entity.search.business.DispatchOrderSearch, com.demo2do.core.entity.page.Page)
     */
    @SuppressWarnings("unchecked")
    public List<DispatchOrder> listDispatch(DispatchOrderSearch dispatchOrderSearch, Page page) {
        
        // 1. build sentence
        String sentence = dispatchOrderSearch.buildSentenceTemplate();
        Map<String, Object> parameters = dispatchOrderSearch.buildParameters();
        
        // 2. do query
        List<DispatchOrder> dispatchOrders = genericDaoSupport.searchForList(sentence, parameters, page.getBeginIndex(), page.getEveryPage());
        page.calculate();
        
        return dispatchOrders;
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.CostService#countShipmentOrder(com.demo2do.fms.admin.entity.search.business.ShipmentOrderSearch)
     */
    public int countTransOrder(TransOrderSearch search) {
        
        String sentence = search.buildSentenceTemplate();
        Map<String, Object> parameters = search.buildParameters();
        
        return genericDaoSupport.count(sentence, parameters);
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.CostService#listShipmentOrder(com.demo2do.fms.admin.entity.search.business.ShipmentOrderSearch, com.demo2do.core.entity.page.Page)
     */
    @SuppressWarnings("unchecked")
    public List<DispatchOrder> listTransOrder(TransOrderSearch search, Page page) {
        
        // 1. build sentence
        String sentence = search.buildSentenceTemplate();
        Map<String, Object> parameters = search.buildParameters();
        
        // 2. do query
        List<DispatchOrder> dispatchOrders = genericDaoSupport.searchForList(sentence, parameters, page.getBeginIndex(), page.getEveryPage());
        page.calculate();
        
        return dispatchOrders;
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.DispatchOrderService#listDispatchOrder(java.util.List)
     */
    public List<DispatchOrder> listDispatchOrder(List<Long> dispatchOrderIds) {
        return dispatchOrderDao.list(dispatchOrderIds);
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.DispatchOrderService#loadDispatchOrder(java.lang.Long)
     */
    public DispatchOrder loadDispatchOrder(Long id) {
        return genericDaoSupport.load(DispatchOrder.class, id);
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.DispatchOrderService#loadDispatchOrder(java.lang.String)
     */
    public DispatchOrder loadDispatchOrder(String loadGroupNumber) {
        return dispatchOrderDao.loadByLoadGroupNumber(loadGroupNumber);
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.DispatchOrderService#loadDispatchOrderRecord(java.lang.Long)
     */
    public DispatchOrderRecord loadDispatchOrderRecord(Long id) {
        return genericDaoSupport.load(DispatchOrderRecord.class, id);
    }

    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.DispatchOrderService#getTransportCost(java.lang.Long)
     */
    public TransportCost getTransportCost(Long dispatchOrderId) {
        return transportCostDao.load(dispatchOrderId);
    }
    
    /*
     * (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.DispatchOrderService#listOtherFeeTypes()
     */
    public List<OtherFeeType> listOtherFeeTypes() {
    	return otherFeeDaoSupport.list();
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.DispatchOrderService#getOtherFeeMappings(java.lang.Long)
     */
    public Map<Long, OtherFeeInfo> getOtherFeeMappings(Long dispatchOrderId) {
        
        // 1. 获取其他费用信息
        List<OtherFeeInfo> otherFeeInfos = otherFeeDaoSupport.getOtherFeeMappings(dispatchOrderId);
        
        // 2. 对费用信息根据id，进行分组
        Map<Long, OtherFeeInfo> otherFeeMap = this.construct(otherFeeInfos);
        return otherFeeMap;
        
    }
    
    /**
     * 
     * @param otherFeeInfos
     * @return
     */
    private Map<Long, OtherFeeInfo> construct(List<OtherFeeInfo> otherFeeInfos){
        
        Map<Long, OtherFeeInfo> otherFeeMap = new LinkedHashMap<Long, OtherFeeInfo>();
        
        for(OtherFeeInfo otherFeeInfo : otherFeeInfos) {
            
            Long feeId = otherFeeInfo.getId();
            BigDecimal fee = otherFeeInfo.getFee();
            Boolean isAnjiSet = otherFeeInfo.getAnjiSet();

            //如果fee_id第一次出现，更新fee与anjiFee
            if  (otherFeeMap.get(feeId) == null) {
                
                otherFeeInfo.editAnjiFeeAndFee(isAnjiSet, fee);
                otherFeeMap.put(feeId, otherFeeInfo);
                
            } else {    //如果fee_id前面已经出现过，更新fee/anjiFee
                
                OtherFeeInfo existOtherFeeInfo = otherFeeMap.get(feeId);
                existOtherFeeInfo.editAnjiFeeOrFee(isAnjiSet, fee);
                otherFeeMap.put(feeId, existOtherFeeInfo);
                
            }
            
        }
        
        return otherFeeMap;
    }

    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.DispatchOrderService#getOtherFee(java.lang.Long)
     */
    public Object getOtherFee(Long dispatchOrderId) {
        return otherFeeDaoSupport.getOtherFee(dispatchOrderId);
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.DispatchOrderService#getOtherFeeDetail(java.lang.Long, java.lang.Long)
     */
    public Object getOtherFeeDetail(Long dispatchOrderId, Long otherFeeTypeId) {
    	return otherFeeDaoSupport.getOtherFeeDetail(dispatchOrderId, otherFeeTypeId);
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.DispatchOrderService#getCarSummary(java.lang.Long)
     */
    public String getCarSummary(Long dispatchOrderId) {
    	return dispatchOrderDaoSupport.getCarSummary(dispatchOrderId);
    }
    
    /*
     * (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.DispatchOrderService#maintain(com.demo2do.fms.entity.business.shipment.DispatchOrder, java.math.BigDecimal, com.demo2do.fms.entity.business.shipment.TransportCost, java.util.List, java.util.List)
     */
    public void maintain(DispatchOrder dispatchOrder, BigDecimal adjustmentDistance, TransportCost transportCost, List<Long> otherFeeTypeIds, List<BigDecimal> otherFees) {
        
        // 1. validate driver and return driver_number
        Long mainDriverId = dispatchOrder.getMainDriverId();
        Long firstViceDriverId = dispatchOrder.getFirstViceDriverId();
        Integer driverNumber = this.validateDriver(mainDriverId, firstViceDriverId);
        
        // 2. count the facility distance by adjustment_distance
        BigDecimal facilityDistance = dispatchOrder.getFacilityDistanceDefault();
        facilityDistance = facilityDistance.add(adjustmentDistance);
        logger.info("#maintain() - 行驶公里数调整后数值={}", facilityDistance);
            
        // 3. update other_fee
        this.editOtherFee(otherFeeTypeIds, otherFees, dispatchOrder);
        
        try {
            
            // FIXME 此处的dispatchOrder持久态对象， 切勿使用更新方法。
            
        	BigDecimal otherFeeSum = this.calculateOtherFee(otherFees);
        	
            // update dispatch_order and transport_cost
            dispatchOrderDaoSupport.edit(dispatchOrder, mainDriverId, firstViceDriverId, driverNumber, facilityDistance, transportCost, otherFeeSum);
            
        } catch (Exception e) {
            
            logger.error("#maintain() - call store procedure occur exception [{}]", e);
            throw new BusinessException("500", "维护派车单失败");
        }
        
    }
    
    /**
     * 
     * @param otherFees
     * @return
     */
    private BigDecimal calculateOtherFee(List<BigDecimal> otherFees) {
    	
    	BigDecimal otherFeeSum = BigDecimal.ZERO;
        for (BigDecimal otherFee : otherFees) {
            
            if(otherFee == null) {
                continue;
            }
            
            otherFeeSum = otherFeeSum.add(otherFee);
            
        }
        
        return otherFeeSum;
    	
    }
    
    /**
     * 检验司机是否已激活
     * 
     * @param dispatchOrder
     * @return
     */
    private Integer validateDriver(Long mainDriverId, Long firstViceDriverId) {
        
        // init driver_number
        Integer driverNumber = 1;
        
        // validate main_driver 
        Employee mainDriver = genericDaoSupport.load(Employee.class, mainDriverId);
        if(!mainDriver.getActive()) {
            throw new BusinessException("主驾驶员未激活");
        }
        
        // validate first_vice_driver 
        if(firstViceDriverId != null && firstViceDriverId != 0){
            
            Employee firstViceDriver = genericDaoSupport.load(Employee.class, firstViceDriverId);
            
            if(!firstViceDriver.getActive())
                throw new BusinessException("第一副驾驶未激活");
            
            driverNumber++;
        }
        
        return driverNumber;
        
    }
    
    /**
     * edit other_fee
     * 
     * @param ids
     * @param otherFees
     * @param dispatchOrder
     */
    private BigDecimal editOtherFee(List<Long> otherFeeTypeIds, List<BigDecimal> otherFees, DispatchOrder dispatchOrder) {
        
        BigDecimal otherFeeSum = BigDecimal.ZERO;
        for (BigDecimal otherFee : otherFees) {
            
            if(otherFee == null) {
                continue;
            }
            
            otherFeeSum = otherFeeSum.add(otherFee);
            
        }
        
        List<Long> allOtherFeeTypeIds = otherFeeDaoSupport.listOtherFeeTypeIds();
        
        for (Long feeTypeId : allOtherFeeTypeIds) {
            
            // 1. 获取该费用类型的费用
            BigDecimal fee = otherFees.get( otherFeeTypeIds.indexOf(feeTypeId) );
            
            fee = (fee == null ? BigDecimal.ZERO : fee);
            
            // 2. 获取该派车单下的，该种其他费用类型，并且不是被安吉设置的其他费用
            OtherFee oldOtherFee = otherFeeDaoSupport.load(feeTypeId, dispatchOrder.getId(), false);
            
            if(oldOtherFee == null){ // 3.0 若不存在，创建
                
                OtherFee otherFee = new OtherFee(feeTypeId, dispatchOrder.getId(), feeTypeId, false, fee);
                genericDaoSupport.save(otherFee);
                
            } else{ // 3.1 若存在，修改费用
                genericDaoSupport.update(oldOtherFee.editOtherFee(fee));
            }
            
        }
        
        return otherFeeSum;
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.DispatchOrderService#disableDispatchOrder(DispatchOrder, Login)
     */
    public void disableDispatchOrder(DispatchOrder dispatchOrder, Login operator) {
        dispatchOrderDaoSupport.disable(dispatchOrder.getId(), operator.getId(), operator.getName());
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.DispatchOrderService#exportAll(com.demo2do.fms.admin.entity.report.business.DispatchOrderReportSearch)
     */
    public List<Map<String, Object>> exportAll(DispatchOrderReportSearch search) {
        String sentence = search.buildSentenceTemplate();
        Map<String, Object> parameters = search.buildParameters();
        RowMapper<Map<String, Object>> rowMapper = search.buildRowMapper();
        return fmsJdbcSupport.queryForList(sentence, parameters, rowMapper);
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.DispatchOrderService#exportAll(com.demo2do.fms.admin.entity.report.business.TransOrderReportSearch)
     */
    public List<Map<String, Object>> exportAll(TransOrderReportSearch search) {
        String sentence = search.buildSentenceTemplate();
        Map<String, Object> parameters = search.buildParameters();
        RowMapper<Map<String, Object>> rowMapper = search.buildRowMapper();
        return fmsJdbcSupport.queryForList(sentence, parameters, rowMapper);
    }

}
